package com.yuyue.springFramework.note08;

import com.yuyue.springFramework.note08.entity.*;
import com.yuyue.springFramework.note08.entity.lifeCycle.LifeCycleBean;
import com.yuyue.springFramework.note08.entity.lifeCycle.MyConfig;
import org.junit.Test;
import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.*;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.util.Arrays;

/**
 * 《Spring-8 Bean的生命周期》文章代码
 *  文章地址：https://mp.weixin.qq.com/s/qzsAVdk7N4xF8gZaE69Yng
 */
public class Note08Test {

    //使用编码的方式获取 BeanDefinition
    @Test
    public void fun1(){
        //使用BeanDefinition的构造工具创建Beandefinition
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(Human.class);
        BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
        System.out.println(beanDefinition);
    }

    //设置Bean属性并获取Bean
    @Test
    public void fun2(){
        //使用BeanDefinition的构造工具创建Beandefinition
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(Human.class);
        //设置Bean属性
        beanDefinitionBuilder.addPropertyValue("name", "鱼跃");
        beanDefinitionBuilder.addPropertyValue("age", 18);
        BeanDefinition beandefinition = beanDefinitionBuilder.getBeanDefinition();
        System.out.println(beandefinition);

        //创建Spring容器
        DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
        //使用 registerBeanDefinition 向容器中注册Bean
        defaultListableBeanFactory.registerBeanDefinition("human", beandefinition);
        //获取Bean
        Human human = defaultListableBeanFactory.getBean("human", Human.class);
        System.out.println(human);

    }

    //组装有依赖关系的Bean
    @Test
    public void fun3(){
        //使用BeanDefinition的构造工具创建Beandefinition
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(Human.class);
        beanDefinitionBuilder.addPropertyValue("name", "鸟飞");
        beanDefinitionBuilder.addPropertyValue("age", 26);
        BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();

        //使用BeanDefinition的构造工具创建Beandefinition
        BeanDefinitionBuilder beanDefinitionBuilder1 = BeanDefinitionBuilder.rootBeanDefinition(House.class);
        beanDefinitionBuilder1.addPropertyValue("name", "汤臣一品");
        beanDefinitionBuilder1.addPropertyValue("addr", "观山湖");
        //设置依赖的Bean
        beanDefinitionBuilder1.addPropertyReference("human", "human");
        BeanDefinition beanDefinition1 = beanDefinitionBuilder1.getBeanDefinition();

        //创建Spring容器
        DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
        //注册Bean
        defaultListableBeanFactory.registerBeanDefinition("human", beanDefinition);
        defaultListableBeanFactory.registerBeanDefinition("house", beanDefinition1);
        //获取Bean
        Human human = defaultListableBeanFactory.getBean("human", Human.class);
        House house = defaultListableBeanFactory.getBean("house", House.class);

        System.out.println(house);
        System.out.println(human);
    }

    //设置父类
    @Test
    public void fun4(){
        //rootBeanDefinition 是没有父类的，因此使用通用 genericBeanDefinition
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(Human.class);
        beanDefinitionBuilder.addPropertyValue("name", "马爸爸");
        beanDefinitionBuilder.addPropertyValue("age", 22);
        BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();

        BeanDefinitionBuilder beanDefinitionBuilder1 = BeanDefinitionBuilder.genericBeanDefinition(Human.class);
        beanDefinitionBuilder1.addPropertyValue("name", "淘宝");
        //定义父bean
        beanDefinitionBuilder1.setParentName("mayun");
        BeanDefinition beanDefinition1 = beanDefinitionBuilder1.getBeanDefinition();

        DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
        //注册bean
        defaultListableBeanFactory.registerBeanDefinition("mayun", beanDefinition);
        defaultListableBeanFactory.registerBeanDefinition("taobao", beanDefinition1);

        //获取Bean
        Human mayun = defaultListableBeanFactory.getBean("mayun", Human.class);
        Human taobao = defaultListableBeanFactory.getBean("taobao", Human.class);

        System.out.println(mayun);
        System.out.println(taobao);

        //阶段4：BeanDefinition合并阶段，对比合并前后的Bean信息
        System.out.println(defaultListableBeanFactory.getBeanDefinition("taobao"));
        System.out.println(defaultListableBeanFactory.getMergedBeanDefinition("taobao"));
    }

    //设置集合属性
    @Test
    public void fun5(){
        //定义Bean human1
        BeanDefinition human1 = BeanDefinitionBuilder.
                genericBeanDefinition(Human.class).
                addPropertyValue("name", "胡歌").
                getBeanDefinition();

        //定义Bean human2
        BeanDefinition human2 = BeanDefinitionBuilder.
                genericBeanDefinition(Human.class).
                addPropertyValue("name", "刘亦菲").
                getBeanDefinition();

        //定义字符串集合
        ManagedList<String> stringList = new ManagedList<>();
        stringList.addAll(Arrays.asList("Spring系列","高并发系列","Maven系列"));

        //定义Bean集合
        ManagedList<RuntimeBeanReference> humanList = new ManagedList<>();
        //运行时Bean引用，类似于xml中的ref
        humanList.add(new RuntimeBeanReference("human1"));
        humanList.add(new RuntimeBeanReference("human2"));

        //定义字符串集合
        ManagedSet<String> stringSet = new ManagedSet<>();
        stringSet.addAll(Arrays.asList("Spring系列","高并发系列","Maven系列"));

        //定义Bean集合
        ManagedSet<RuntimeBeanReference> humanSet = new ManagedSet<>();
        humanSet.add(new RuntimeBeanReference("human1"));
        humanSet.add(new RuntimeBeanReference("human2"));

        //定义字符串集合
        ManagedMap<String, String> stringMap = new ManagedMap<>();
        stringMap.put("系列1", "Spring系列");
        stringMap.put("系列2", "高并发系列");
        stringMap.put("系列3", "Maven系列");

        //定义Bean集合
        ManagedMap<String, RuntimeBeanReference> humanMap = new ManagedMap<>();
        humanMap.put("human1", new RuntimeBeanReference("human1"));
        humanMap.put("human2", new RuntimeBeanReference("human2"));

        //使用原生API创建 BeanDefinition，并填充属性值
        GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
        genericBeanDefinition.setBeanClassName(CompositeObj.class.getName());
        genericBeanDefinition.getPropertyValues().add("name", "鱼跃").
                add("salary", 50000).
                add("human1", new RuntimeBeanReference("human1")).
                add("stringList", stringList).
                add("humanList", humanList).
                add("stringSet", stringSet).
                add("humanSet", humanSet).
                add("stringMap", stringMap).
                add("stringHumanMap", humanMap);

        //注册Bean
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        factory.registerBeanDefinition("human1", human1);
        factory.registerBeanDefinition("human2", human2);
        factory.registerBeanDefinition("genericBeanDefinition", genericBeanDefinition);

        //遍历Bean
        for (String beanName:
             factory.getBeanDefinitionNames()) {
            System.out.println(String.format("%S->%S", beanName, factory.getBean(beanName)));
        }
    }

    @Test
    //XML元信息解析
    public void fun6(){
        //定义一个spring容器，这个容器默认实现了BeanDefinitionRegistry，所以本身就是一个bean注册器
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        //定义XML格式的BeanDefinition读取器，需传入注册器
        XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(factory);
        //加载指定BeanDefinition配置文件
        int count = xmlBeanDefinitionReader.loadBeanDefinitions("ApplicationContext05.xml");
        System.out.println(String.format("共注册了 %S 个Bean", count));

        //遍历注册的Bean信息
        for (String beanName:
             factory.getBeanDefinitionNames()) {
            BeanDefinition beandefinition = factory.getBeanDefinition(beanName);

            String beadDefinitionClassName = beandefinition.getClass().getName();

            Object bean = factory.getBean(beanName);

            System.out.println(beanName + ":");
            System.out.println("beanDefinitionClassName: " + beadDefinitionClassName);
            System.out.println("beandefinition: " + beandefinition);
            System.out.println("bean: " + bean);

        }
    }

    @Test
    //properties元信息解析
    public void fun7(){
        //定义一个spring容器，这个容器默认实现了BeanDefinitionRegistry，所以本身就是一个bean注册器
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        //定义properties格式的BeanDefinition读取器，需传入注册器
        PropertiesBeanDefinitionReader propertiesBeanDefinitionReader = new PropertiesBeanDefinitionReader(factory);
        //加载指定BeanDefinition配置文件
        int count = propertiesBeanDefinitionReader.loadBeanDefinitions("bean.properties");
        System.out.println(String.format("共注册了 %S 个Bean", count));

        //遍历注册的Bean信息
        for (String beanName:
                factory.getBeanDefinitionNames()) {
            BeanDefinition beandefinition = factory.getBeanDefinition(beanName);

            String beadDefinitionClassName = beandefinition.getClass().getName();

            Object bean = factory.getBean(beanName);

            System.out.println(beanName + ":");
            System.out.println("beanDefinitionClassName: " + beadDefinitionClassName);
            System.out.println("beandefinition: " + beandefinition);
            System.out.println("bean: " + bean);
        }
    }

    @Test
    //注解元信息解析
    public void fun8(){
        //定义一个spring容器，这个容器默认实现了BeanDefinitionRegistry，所以本身就是一个bean注册器
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        //定义注解格式的BeanDefinition读取器，需传入注册器
        AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(factory);
        //注册bean
        reader.register(Service1.class, Service2.class);

        //没有此行代码，Service2中不会自动注入Service1，详细原因见后文
        factory.getBeansOfType(BeanPostProcessor.class).values().forEach(factory::addBeanPostProcessor);

        //遍历注册的Bean信息
        for (String beanName:
             factory.getBeanDefinitionNames()) {
            //仅打印非Spring本身的Bean
            if (beanName.startsWith("org.")){
                continue;
            }

            BeanDefinition beandefinition = factory.getBeanDefinition(beanName);

            String beadDefinitionClassName = beandefinition.getClass().getName();

            Object bean = factory.getBean(beanName);

            System.out.println(beanName + ":");
            System.out.println("beanDefinitionClassName: " + beadDefinitionClassName);
            System.out.println("beandefinition: " + beandefinition);
            System.out.println("bean: " + bean);
        }
    }

    @Test
    //默认注册器
    public void fun9(){
        //定义一个spring容器，这个容器默认实现了BeanDefinitionRegistry，所以本身就是一个bean注册器
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        //通用Bean定义
        GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
        genericBeanDefinition.setBeanClass(String.class);
        genericBeanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, "鱼跃鸟飞");

        //注册Bean
        factory.registerBeanDefinition("yuyue", genericBeanDefinition);

        System.out.println("含有Bean：" + factory.containsBeanDefinition("yuyue"));

        System.out.println("所有Bean定义：" + Arrays.asList(factory.getBeanDefinitionNames()));

        System.out.println("Bean个数：" + factory.getBeanDefinitionCount());

        System.out.println("Bean名称是否被使用：" + factory.isBeanNameInUse("yuyue"));

        //注册别名
        factory.registerAlias("yuyue", "feiniao");

        System.out.println("别名是否被使用：" + factory.isAlias("feiniao"));

        System.out.println("别名列表：" + Arrays.asList(factory.getAliases("yuyue")));

        System.out.println("Bean：" + factory.getBean("yuyue"));
    }

    @Test
    //生命周期涉及的Bean后处理器汇总案例
    public void fun10(){
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        //注册bean
        context.register(MyConfig.class);
        context.refresh();

        context.getBean(LifeCycleBean.class);

        context.close();
    }

    @Test
    //阶段6：实例化前案例
    public void fun11(){
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        //添加实例化后处理器
        factory.addBeanPostProcessor(new InstantiationAwareBeanPostProcessor() {
            @Override
            //重新实例化前方法
            public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
                //Human类注册Bean时，返回指定的对象
                if (beanClass == Human.class){
                    Human human = new Human();
                    human.setName("胡歌");
                    return human;
                }
                //其他类不处理
                return null;
            }
        });

        //创建Human类的Bean定义
        BeanDefinition beanDefinition = BeanDefinitionBuilder.
                genericBeanDefinition(Human.class).
                addPropertyValue("name", "景天").
                getBeanDefinition();
        //注册Human类
        factory.registerBeanDefinition("human", beanDefinition);
        //获取注册结果
        System.out.println(factory.getBean("human"));
    }

    @Test
    //阶段7：实例化案例
    public void fun12(){
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        //将自定义指定构造器的后处理器添加到容器
        factory.addBeanPostProcessor(new MySmartInstantiationAwareBeanPostProcessor());

        //注册human及所需的所有参数
        factory.registerBeanDefinition("name",
                BeanDefinitionBuilder.
                        genericBeanDefinition(String.class).
                        addConstructorArgValue("鱼跃飞鸟").
                        getBeanDefinition());
        factory.registerBeanDefinition("age",
                BeanDefinitionBuilder.
                        genericBeanDefinition(Integer.class).
                        addConstructorArgValue(18).
                        getBeanDefinition());
        factory.registerBeanDefinition("human",
                BeanDefinitionBuilder.
                        genericBeanDefinition(Human.class).
                        getBeanDefinition());

        //获取并打印human，根据注入的参数结果可以判断使用了那个构造器
        Human human = factory.getBean("human", Human.class);
        System.out.println(human);
    }

    @Test
    //阶段9：阻止依赖注入
    public void fun13(){
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

        //增加实例化后处理器，重新实例化后是否填充依赖值方法
        factory.addBeanPostProcessor(new InstantiationAwareBeanPostProcessor() {
            @Override
            public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
                //指定bean名称不填充依赖
                if("human1".equals(beanName)){
                    return false;
                }
                return true;
            }
        });

        //注册Bean
        factory.registerBeanDefinition("human1",
                BeanDefinitionBuilder.
                        genericBeanDefinition(Human.class).
                        addPropertyValue("name", "刘亦菲").
                        addPropertyValue("age", 18).
                        getBeanDefinition());
        factory.registerBeanDefinition("human2",
                BeanDefinitionBuilder.
                        genericBeanDefinition(Human.class).
                        addPropertyValue("name", "胡歌").
                        addPropertyValue("age", 20).
                        getBeanDefinition());

        //获取并打印bean
        System.out.println(factory.getBean("human1"));
        System.out.println(factory.getBean("human2"));
    }

    @Test
    //阶段10：依赖注入
    public void fun14(){
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

        //覆盖依赖注入
        factory.addBeanPostProcessor(new InstantiationAwareBeanPostProcessor() {
            @Override
            public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
                if("human1".equals(beanName)){
                    if (pvs == null){
                        pvs = new MutablePropertyValues();
                    }

                    if (pvs instanceof MutablePropertyValues){
                        MutablePropertyValues mpvs = (MutablePropertyValues) pvs;
                        //填充属性值
                        mpvs.add("name", "路人甲");
                        mpvs.add("age", 18);
                    }
                }
                return null;
            }
        });

        //注册Bean
        factory.registerBeanDefinition("human1",
                BeanDefinitionBuilder
                        .genericBeanDefinition(Human.class).
                        addPropertyValue("name", "刘亦菲").
                        getBeanDefinition());
        factory.registerBeanDefinition("human2",
                BeanDefinitionBuilder
                        .genericBeanDefinition(Human.class).
                        addPropertyValue("name", "胡歌").
                        addPropertyValue("age", 20).
                        getBeanDefinition());

        //获取并打印bean
        System.out.println(factory.getBean("human1"));
        System.out.println(factory.getBean("human2"));
    }

    @Test
    //阶段11：初始化前
    public void fun15(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.register(MyAwreLoad.class);
        applicationContext.refresh();
    }

    @Test
    //阶段12：初始化
    public void fun16(){
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        factory.registerBeanDefinition("myInitBean",
                BeanDefinitionBuilder.
                        genericBeanDefinition(MyInitBean.class).
                        //设置初始化方法名称
                        setInitMethodName("init").
                        getBeanDefinition());

        System.out.println(factory.getBean("myInitBean"));
    }

}
